package com.hmdp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.User;
import com.hmdp.mapper.UserMapper;
import com.hmdp.service.IUserService;
import com.hmdp.utils.MyUtils;
import com.hmdp.utils.RegexUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;

import java.util.HashMap;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import static com.hmdp.utils.RedisConstants.*;
import static com.hmdp.utils.SystemConstants.USER_NICK_NAME_PREFIX;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author HSJ
 * @since 2023-02-22
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Result sendCode(String phone, HttpSession session) {
        log.debug(">>>>>> 验证码发送功能   1.校验手机号");
        if (RegexUtils.isPhoneInvalid(phone)) {
            log.debug("手机号不符合格式  返回错误");
            return Result.fail("手机号格式错误");
        } else {
            log.debug("手机号符合格式 生成验证码");
            String code = RandomUtil.randomNumbers(6);
            //保存到 session  ,session.setAttribute("code", code);
            //但session无法在 多tomcat保证数据一致性， 所以采用redis, 以业务线 + 手机号 做为 key 可以保证key的唯一 ，且设置有效期 ，避免redis一直存着不是放
            log.debug("将 验证码 放入到 redis 中");
            stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY+phone,code,2, TimeUnit.MINUTES);
            // 可以用阿里云的短信功能实现 验证码发送
            log.debug("验证码发送成功,验证码为:{} <<<<<<<<", code);
            //返回ok
            return Result.ok();
        }
    }

    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) throws IllegalAccessException {
            log.debug(" >>>>>> 登录功能  1.先再次校验手机号");
           String phone = loginForm.getPhone();

           if (RegexUtils.isPhoneInvalid(phone)){
                log.debug("//验验证码格式，验证码不符号规则 ，返回错误");
               return Result.fail("手机号格式错误");
           }
            log.debug(" //2.校验验证码");
            log.debug("从redis 拿到 验证码");
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
        if (cacheCode == null || !cacheCode.equals(loginForm.getCode())){
                log.debug(" //验证码 不存在，或 用户输入验证码 和 redis存的验证码 不一致 返回失败");
                 return  Result.fail("验证码错误");
             }
          log.debug("// 3.验证码校验成功， 根据手机号查用户");
              User user = query().eq("phone",phone).one();
          log.debug("//判断用户是否存在");
              if (user ==null){
                log.debug("  // 不存在 创建用户 保存数据库");
                 user =  createUserWithPhone(phone);
              }
           log.debug("// 4. 用户已存在 或 已存入新用户  保存用户信息到redis");
           log.debug(" 生成随机token，作为Cookie、使客户端每次请求带着token, 这样就可以通过token找到redis中存的user");
           String token = UUID.randomUUID().toString();
           log.debug("为避免redis存放过多user信息，可能造成数据安全问题，也浪费资源空间，将user的id等信息封装为 UserDTO存入redis");
           UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
           log.debug("userDTO存入 使用redis的 hash类型, 设置有效期30 分钟");
           String tokenKey = LOGIN_USER_KEY+token;
           stringRedisTemplate.opsForHash().putAll(tokenKey, BeanUtil.beanToMap(userDTO,new HashMap<>(),
                   CopyOptions.create().setIgnoreNullValue(true)
                           .setFieldValueEditor((fieldName,fieldValue)-> fieldValue.toString())));
           stringRedisTemplate.expire(tokenKey,LOGIN_USER_TTL,TimeUnit.MINUTES);
           log.debug("发送给前端的token{}",token);
           log.debug("登录逻辑 结束 <<<<<<<<<<<<<<<<<");
        return Result.ok(token);
    }

    private User createUserWithPhone(String phone) {
         User user = new User();
         user.setPhone(phone);
         user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
         save(user);
         return user;
    }




}


